package com.yunpeng.aidevice.dao.service.impl;

import com.yunpeng.aidevice.common.constant.ConfigConstantValues;
import com.yunpeng.aidevice.common.dto.save.DeviceSaveDTO;
import com.yunpeng.aidevice.common.enums.DeviceStateEnum;
import com.yunpeng.aidevice.common.enums.DeviceTypeEnum;
import com.yunpeng.aidevice.common.enums.ResultCodeEnum;
import com.yunpeng.aidevice.dao.mapper.entity.DeviceMapper;
import com.yunpeng.aidevice.dao.mapper.entity.OrganizationMapper;
import com.yunpeng.aidevice.dao.mapper.record.RecordMapper;
import com.yunpeng.aidevice.dao.mapper.relation.DeviceRelationOrganizationMapper;
import com.yunpeng.aidevice.dao.model.MerchantCountDO;
import com.yunpeng.aidevice.dao.model.entity.DeviceDO;
import com.yunpeng.aidevice.dao.model.entity.OrganizationDO;
import com.yunpeng.aidevice.dao.model.record.RecordDO;
import com.yunpeng.aidevice.dao.model.relation.DeviceRelationOrganizationDO;
import com.yunpeng.aidevice.dao.service.ICodeGenerateCommonService;
import com.yunpeng.aidevice.dao.service.IDeviceService;
import com.yunpeng.cache.util.DistributedLockHelper;
import com.yunpeng.common.exception.BizException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;

/**
 * DeviceServiceImpl
 *
 * @author : Moore(hemw)
 * @date : 2020-04-27
 */
@Service
public class DeviceServiceImpl implements IDeviceService {

    /**
     * 日志对象
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(DeviceServiceImpl.class);
    /**
     * 当前服务的实体表名
     */
    private static final String TABLE_NAME = "t_ai_device";

    @Resource
    private ICodeGenerateCommonService codeGenerateCommonService;

    @Resource
    private DeviceMapper deviceMapper;

    @Resource
    private OrganizationMapper organizationMapper;

    @Resource
    private DeviceRelationOrganizationMapper deviceRelationOrganizationMapper;

    @Resource
    private RecordMapper recordMapper;

    @Override
    /**
     * 新增设备
     *
     * @param deviceSaveDTO 设备信息
     * @param operatorCode  操作人代码
     * @return
     */
    public Boolean addNew(DeviceSaveDTO deviceSaveDTO, String operatorCode) {
        if (deviceSaveDTO == null || StringUtils.isBlank(deviceSaveDTO.getDeviceNumber())) {
            return false;
        }
        if (StringUtils.isBlank(deviceSaveDTO.getDeviceId())) {
            throw new BizException(ResultCodeEnum.LACK_DEVICE_ID_FAILED);
        }
        //number 验证
        DeviceDO deviceDO = deviceMapper.getByNumber(deviceSaveDTO.getDeviceNumber(), deviceSaveDTO.getMerchantCode(), deviceSaveDTO.getTenantCode());
        if (deviceDO != null) {
            throw new BizException(ResultCodeEnum.EXISTS_NUMBER_FAILED);
        }
        deviceDO = deviceMapper.getByDeviceId(deviceSaveDTO.getDeviceId());
        if (deviceDO != null) {
            throw new BizException(ResultCodeEnum.EXISTS_DEVICE_ID_FAILED);
        }
        deviceDO = new DeviceDO();
        deviceDO.initForNew(operatorCode);
        deviceDO.setState(deviceSaveDTO.getDisabled() ? DeviceStateEnum.disabled.getValue() : DeviceStateEnum.normal.getValue());
        deviceDO.setInstallTime(LocalDateTime.now());
        deviceDO.setInstallLocation(deviceSaveDTO.getInstallLocation());
        deviceDO.setMerchantCode(deviceSaveDTO.getMerchantCode());
        deviceDO.setTenantCode(deviceSaveDTO.getTenantCode());
        deviceDO.setName(deviceSaveDTO.getDeviceName());
        deviceDO.setNumber(deviceSaveDTO.getDeviceNumber());
        deviceDO.setDeviceId(deviceSaveDTO.getDeviceId().toLowerCase());
        deviceDO.setDeviceType(DeviceTypeEnum.TMAC.getValue());
        //处理组织
        List<DeviceRelationOrganizationDO> deviceRelationOrganizationDOList = processGetOrgList(deviceSaveDTO, operatorCode);
        if (DistributedLockHelper.INSTANCE.getLock(TABLE_NAME, ConfigConstantValues.APPLICATION_NAME)) {
            deviceDO.setCode(codeGenerateCommonService.generateCode(TABLE_NAME));
            //新增时，需要重新写入设备代码
            for (DeviceRelationOrganizationDO deviceRelationOrganizationDO : deviceRelationOrganizationDOList) {
                deviceRelationOrganizationDO.setDeviceCode(deviceDO.getCode());
            }
            try {
                saveNewDevice(deviceDO, deviceRelationOrganizationDOList);
                return true;
            } catch (Exception ex) {
                LOGGER.error("save device info error : {}", deviceDO, ex);
                throw new BizException(ResultCodeEnum.SAVE_DATA_FAILED);
            } finally {
                codeGenerateCommonService.holdCountMinus(TABLE_NAME);
                DistributedLockHelper.INSTANCE.unlock(TABLE_NAME, ConfigConstantValues.APPLICATION_NAME);
            }
        } else {
            return false;
        }
    }

    @Override
    /**
     * 更新设备信息
     *
     * @param deviceSaveDTO 设备信息
     * @param operatorCode  操作人代码
     * @return
     */
    public Boolean updateDevice(DeviceSaveDTO deviceSaveDTO, String operatorCode) {
        if (deviceSaveDTO == null || StringUtils.isBlank(deviceSaveDTO.getDeviceCode())) {
            return false;
        }
        DeviceDO deviceDO = getByCode(deviceSaveDTO.getDeviceCode(), deviceSaveDTO.getMerchantCode(), deviceSaveDTO.getTenantCode());
        if (deviceDO == null) {
            throw new BizException(ResultCodeEnum.NOT_EXISTS_DATA_FAILED, "无此设备");
        }
        Map<String, Object> updateMap = new HashMap<>(16);
        updateMap.put("updater", operatorCode);
        updateMap.put("update_time", new Date());
        if (StringUtils.isNotBlank(deviceSaveDTO.getDeviceNumber())) {
            if (!deviceDO.getNumber().equals(deviceSaveDTO.getDeviceNumber())) {
                if (deviceMapper.countNumberExistsExcludeCode(deviceSaveDTO.getDeviceNumber(), deviceDO.getCode(), deviceDO.getMerchantCode(), deviceDO.getTenantCode()) > 0) {
                    throw new BizException(ResultCodeEnum.EXISTS_NUMBER_FAILED);
                }
                updateMap.put("number", deviceSaveDTO.getDeviceNumber());
            }
        }
        if (StringUtils.isNotEmpty(deviceSaveDTO.getDeviceName())) {
            updateMap.put("name", deviceSaveDTO.getDeviceName());
        }
        if (StringUtils.isNotEmpty(deviceSaveDTO.getInstallLocation())) {
            updateMap.put("install_location", deviceSaveDTO.getInstallLocation());
            //未启用，视为重新安装
            if (DeviceStateEnum.disabled.getValue().equals(deviceDO.getState())) {
                updateMap.put("install_location", deviceSaveDTO.getInstallLocation());
            }
        }
        if (deviceSaveDTO.getDisabled()) {
            updateMap.put("state", DeviceStateEnum.disabled.getValue());
        } else {
            updateMap.put("state", DeviceStateEnum.normal.getValue());
        }
        //处理组织
        List<DeviceRelationOrganizationDO> deviceRelationOrganizationDOList = processGetOrgList(deviceSaveDTO, operatorCode);
        try {
            updateDevice(deviceDO.getId(), deviceDO.getCode(), updateMap, deviceRelationOrganizationDOList);
            return true;
        } catch (Exception ex) {
            LOGGER.error("save device info error : {}", deviceDO, ex);
            throw new BizException(ResultCodeEnum.SAVE_DATA_FAILED);
        }
    }

    @Override
    /**
     * 禁用启用设备
     *
     * @param disable      是否禁用
     * @param deviceCode   设备代码
     * @param operatorCode 操作人代码
     * @param merchantCode 商户代码
     * @param tenantCode   租户代码
     * @return
     */
    public Boolean enableDisableDevice(Boolean disable, String deviceCode, String operatorCode, String merchantCode, String tenantCode) {
        if (disable == null) {
            return false;
        }
        DeviceDO deviceDO = getByCode(deviceCode, merchantCode, tenantCode);
        if (deviceDO == null) {
            throw new BizException(ResultCodeEnum.NOT_EXISTS_DATA_FAILED, "无此设备");
        }
        Map<String, Object> updateMap = new HashMap<>(16);
        updateMap.put("updater", operatorCode);
        updateMap.put("update_time", new Date());
        if (disable) {
            updateMap.put("state", DeviceStateEnum.disabled.getValue());
        } else {
            updateMap.put("state", DeviceStateEnum.normal.getValue());
        }
        return deviceMapper.update(deviceDO.getId(), updateMap) > 0;
    }

    @Override
    /**
     * 删除设备
     *
     * @param deviceCode   设备代码
     * @param merchantCode 商户代码
     * @param tenantCode   租户代码
     * @return
     */
    public Boolean deleteDevice(String deviceCode, String merchantCode, String tenantCode) {
        DeviceDO deviceDO = getByCode(deviceCode, merchantCode, tenantCode);
        if (deviceDO == null) {
            throw new BizException(ResultCodeEnum.NOT_EXISTS_DATA_FAILED, "无此设备");
        }
        deviceMapper.delete(deviceDO.getId());
        deviceRelationOrganizationMapper.deleteByDeviceCode(deviceDO.getCode());
        return true;
    }

    @Override
    /**
     * 获取设备对象
     *
     * @param deviceCode   设备代码
     * @param merchantCode 商户代码
     * @param tenantCode   租户代码
     * @return
     */
    public DeviceDO getByCode(String deviceCode, String merchantCode, String tenantCode) {
        if (StringUtils.isBlank(deviceCode)) {
            return null;
        }
        if (StringUtils.isBlank(merchantCode)) {
            return null;
        }
        if (StringUtils.isBlank(tenantCode)) {
            return null;
        }
        DeviceDO deviceDO = deviceMapper.getByCode(deviceCode);
        if (merchantCode.equals(deviceDO.getMerchantCode()) && tenantCode.equals(deviceDO.getTenantCode())) {
            return deviceDO;
        }
        return null;
    }

    @Override
    /**
     * 统计商户设备数量
     *
     * @param merchantCodes
     * @param tenantCode
     * @param state
     * @return
     */
    public List<MerchantCountDO> countByMerchant(List<String> merchantCodes, String tenantCode, String state) {
        return deviceMapper.countByMerchant(merchantCodes, tenantCode, state);
    }

    @Override
    /**
     * 获取商户设备列表（最大2000）
     *
     * @param merchantCode
     * @param tenantCode
     * @return
     */
    public List<DeviceDO> getByMerchant(String merchantCode, String tenantCode) {
        if (StringUtils.isBlank(merchantCode)) {
            return new ArrayList<>();
        }
        if (StringUtils.isBlank(tenantCode)) {
            return new ArrayList<>();
        }
        Map<String, Object> queryMap = new HashMap<>(16);
        queryMap.put("flag", 1);
        queryMap.put("merchant_code", merchantCode);
        queryMap.put("tenant_code", tenantCode);
        return deviceMapper.queryForList(queryMap);
    }

    @Override
    /**
     * 根据设备标识获取对象
     * @param deviceId
     * @return
     */
    public DeviceDO getByDeviceId(String deviceId) {
        if (StringUtils.isBlank(deviceId)) {
            return null;
        }
        return deviceMapper.getByDeviceId(deviceId.toLowerCase());
    }

    @Override
    /**
     * 保存上传记录
     *
     * @param recordDO
     * @return
     */
    public Boolean addRecord(RecordDO recordDO) {
        try {
            recordMapper.add(recordDO);
            return true;
        } catch (Exception ex) {
            LOGGER.error("save record error,", ex);
            return false;
        }
    }

    //region 私有方法

    /**
     * 事务保存设备对象
     *
     * @param deviceDO
     * @param deviceRelationOrganizationDOList
     */
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 3600, rollbackFor = Exception.class)
    void saveNewDevice(DeviceDO deviceDO, List<DeviceRelationOrganizationDO> deviceRelationOrganizationDOList) {
        deviceMapper.add(deviceDO);
        if (deviceRelationOrganizationDOList != null && deviceRelationOrganizationDOList.size() > 0) {
            deviceRelationOrganizationMapper.batchAdd(deviceRelationOrganizationDOList);
        }
    }

    /**
     * 事务保存设备对象
     *
     * @param deviceId
     * @param deviceCode
     * @param updateMap
     * @param deviceRelationOrganizationDOList
     */
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 3600, rollbackFor = Exception.class)
    void updateDevice(Long deviceId, String deviceCode, Map<String, Object> updateMap, List<DeviceRelationOrganizationDO> deviceRelationOrganizationDOList) {
        deviceMapper.update(deviceId, updateMap);
        deviceRelationOrganizationMapper.deleteByDeviceCode(deviceCode);
        if (deviceRelationOrganizationDOList != null && deviceRelationOrganizationDOList.size() > 0) {
            deviceRelationOrganizationMapper.batchAdd(deviceRelationOrganizationDOList);
        }
    }

    /**
     * 处理并获取关联组织列表
     *
     * @param deviceSaveDTO
     * @param operatorCode
     */
    private List<DeviceRelationOrganizationDO> processGetOrgList(DeviceSaveDTO deviceSaveDTO, String operatorCode) {
        List<DeviceRelationOrganizationDO> deviceRelationOrganizationDOList = new ArrayList<>();
        if (deviceSaveDTO.getOrgCodes() != null && deviceSaveDTO.getOrgCodes().size() > 0) {
            List<OrganizationDO> codeDoList = organizationMapper.getByCodes(deviceSaveDTO.getMerchantCode(), deviceSaveDTO.getTenantCode(), deviceSaveDTO.getOrgCodes());
            if (codeDoList != null && codeDoList.size() > 0) {
                for (OrganizationDO organizationDO : codeDoList) {
                    DeviceRelationOrganizationDO deviceRelationOrganizationDO = new DeviceRelationOrganizationDO();
                    deviceRelationOrganizationDO.initForNew(operatorCode);
                    deviceRelationOrganizationDO.setOrganizationCode(organizationDO.getCode());
                    deviceRelationOrganizationDO.setOrganizationName(organizationDO.getName());
                    deviceRelationOrganizationDO.setDeviceCode(deviceSaveDTO.getDeviceCode());
                    deviceRelationOrganizationDOList.add(deviceRelationOrganizationDO);
                }
            }
        } else if (deviceSaveDTO.getOrgNumbers() != null && deviceSaveDTO.getOrgNumbers().size() > 0) {
            List<OrganizationDO> numberDoList = organizationMapper.getByNumbers(deviceSaveDTO.getMerchantCode(), deviceSaveDTO.getTenantCode(), deviceSaveDTO.getOrgNumbers());
            if (numberDoList != null && numberDoList.size() > 0) {
                for (OrganizationDO organizationDO : numberDoList) {
                    DeviceRelationOrganizationDO deviceRelationOrganizationDO = new DeviceRelationOrganizationDO();
                    deviceRelationOrganizationDO.initForNew(operatorCode);
                    deviceRelationOrganizationDO.setOrganizationCode(organizationDO.getCode());
                    deviceRelationOrganizationDO.setOrganizationName(organizationDO.getName());
                    deviceRelationOrganizationDO.setDeviceCode(deviceSaveDTO.getDeviceCode());
                    deviceRelationOrganizationDOList.add(deviceRelationOrganizationDO);
                }
            }
        }
        return deviceRelationOrganizationDOList;
    }
    //endregion
}
